
/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    fb.c
 * @brief   支持Framebuffer的LCD设备文件
 * @author  K
 * @version 0.0.1
 * @date    2021-07-02
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-07-02   | v0.0.1    | Kcode   | 支持Framebuffer的LCD设备文件
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <config.h>
#include <disp_manager.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>

#include "debug_manager.h"

static int s_FBFD;
static int s_ScreenSize;
static int s_LineWidth;
static int s_PixelWidth;
static struct fb_var_screeninfo s_tVar;
static struct fb_fix_screeninfo s_tFix;
static unsigned char *s_pFbmem;

static int FBDeviceInit(void);
static int FBShowPixel(int PenX, int PenY, unsigned int Color);
static int FBCleanScreen(unsigned int BackColor);
static int FBShowPage(PT_PIXELDATAS pixel_datas);

static T_DispOpr s_tFBDispOpr = {
	.name         = "fb",
	.DeviceInit   = FBDeviceInit,
	.ShowPixel    = FBShowPixel,
	.CleanScreen  = FBCleanScreen,
	.ShowPage	  = FBShowPage,
};

/*!
 * @brief  "framebuffer显示设备"的初始化函数
 * @param  [in] 无
 * @return 0：注册成功，其他值：失败
 */
static int FBDeviceInit(void)
{
	/* 打开设备：支持读写 */
	s_FBFD = open(FB_DEVICE_NAME, O_RDWR);
	if (s_FBFD < 0) {
		DBG_PRINTF("can not open %s , err code :%d\n", FB_DEVICE_NAME, s_FBFD);
		return -1;
	}

	/* 获得可变信息 */
	if (ioctl(s_FBFD, FBIOGET_VSCREENINFO, &s_tVar)) {
		DBG_PRINTF("can not get s_tVar\n");
		return -1;
	}

	/* 获得固定信息 */
	if (ioctl(s_FBFD, FBIOGET_FSCREENINFO, &s_tFix)) {
		DBG_PRINTF("can not get s_tVar\n");
		return -1;
	}
	
	s_ScreenSize = s_tVar.xres * s_tVar.yres * s_tVar.bits_per_pixel / 8; // 屏幕总像素所占的字节数
	s_LineWidth   = s_tVar.xres * s_tVar.bits_per_pixel / 8;	// 每行像素所占的字节数
	s_PixelWidth  = s_tVar.bits_per_pixel / 8;	// 每个像素所占字节数

	s_tFBDispOpr.Xres 		= s_tVar.xres;
	s_tFBDispOpr.Yres 		= s_tVar.yres;
	s_tFBDispOpr.Bpp   		= s_tVar.bits_per_pixel;
	s_tFBDispOpr.LineWidth = s_LineWidth;
		
	/* 直接映射到内存的Framebuffer */
	s_pFbmem = (unsigned char *)mmap(NULL, s_ScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, s_FBFD, 0);
	if (s_pFbmem == (unsigned char *)-1) {
		DBG_PRINTF("can not mmap\n");
		return -1;
	}
	s_tFBDispOpr.pDispMem = s_pFbmem;

	return 0;
}

/*!
 * @brief  设置FrameBuffer的指定像素为某颜色
 * @param  PenX[in] 像素的x坐标
 * @param  PenY[in] 像素的y坐标
 * @param  Color[in] 颜色值,格式为32Bpp,即0x00RRGGBB
 * @return 0：成功，其他值：失败
 */
static int FBShowPixel(int PenX, int PenY, unsigned int Color)
{
	unsigned char  *pPen8;
	unsigned short *pPen16;
	unsigned int   *pPen32;
	unsigned int	red, green, blue;
	
	if ((PenX >= s_tVar.xres) || (PenY >= s_tVar.yres))
	{
		DBG_PRINTF("out of region\n");
		return -1;
	}

	/* 该坐标在内存中对应像素的位置 */
	pPen8  = s_pFbmem + PenY * s_LineWidth + PenX * s_PixelWidth;
	pPen16 = (unsigned short *)pPen8;
	pPen32 = (unsigned int   *)pPen8;

	switch (s_tFBDispOpr.Bpp) {
	case 8:
		*pPen8 = (unsigned char)Color;
		break;
	case 16:
		/* RGB:565 */
		red      = ((Color >> 16) & 0xff) >> 3;
		green    = ((Color >> 8 )  & 0xff) >> 2;
		blue     = ((Color >> 0 )  & 0xff) >> 3;
		*pPen16  = (red << 11) | (green << 5) | blue;
		break;
	case 32:
		*pPen32 = Color;
		break;
	default:
		DBG_PRINTF("can not surport %d bpp\n", s_tFBDispOpr.Bpp);
		return -1;
	}

	return 0;
}

/*!
 * @brief  "framebuffer显示设备"的清屏函数
 * @param  BackColor[in] 整个屏幕设置为该颜色
 * @return 0：成功，其他值：失败
 */
static int FBCleanScreen(unsigned int BackColor)
{
	int i;
	unsigned char  *pPen8;
	unsigned short *pPen16;
	unsigned int   *pPen32;
	unsigned int	red, green, blue;

	pPen8  = s_pFbmem;
	pPen16 = (unsigned short *)pPen8;
	pPen32 = (unsigned int   *)pPen8;

	switch (s_tFBDispOpr.Bpp) {
	case 8:
		memset(pPen8, BackColor, s_ScreenSize);
		break;
	case 16:
		/* RGB:565 */
		red      = ((BackColor >> 16) & 0xffff) >> 3;
		green    = ((BackColor >> 8 )  & 0xffff) >> 2;
		blue     = ((BackColor >> 0 )  & 0xffff) >> 3;
		BackColor = (red << 11) | (green << 5) | blue;
		
		for (i = 0; i < s_ScreenSize;) {
			*pPen16 = BackColor;
			pPen16++;
			i += 2;
		}
		break;
	case 32:
		for (i = 0; i < s_ScreenSize;) {
			*pPen32 = BackColor;
			pPen32++;
			i += 4;
		}
		break;
	default:
		DBG_PRINTF("can not surport %dbpp\n", s_tFBDispOpr.Bpp);
		return -1;
	}

	return 0;
}

/*!
 * @brief  把PT_VideoMem中的颜色数据在FrameBuffer上显示出来
 * @param  pixel_datas[in] 内含整屏的象素数据
 * @return 0：成功，其他值：失败
 */
 static int FBShowPage(PT_PIXELDATAS pixel_datas)
{
    /* 瀵逛簬鍏朵粬鏄剧ず鍣紝濡俈GA鏍煎紡 */
    if (s_tFBDispOpr.pDispMem != pixel_datas->PixelDatas)
    {
	    memcpy(s_tFBDispOpr.pDispMem, pixel_datas->PixelDatas, pixel_datas->TotalBytes);
    }
    
	return 0;
}
 
/*!
 * @brief  注册"framebuffer显示设备"
 * @param  [in] 无
 * @return 0：成功，其他值：失败
 */
int FBInit(void)
{
	return RegisterDispOpr(&s_tFBDispOpr);
}

